package org.sean.framework.canary.feign;

import com.google.common.util.concurrent.ThreadFactoryBuilder;
import org.sean.framework.logging.Logger;
import org.sean.framework.web.holder.RequestHolder;
import org.slf4j.MDC;

import java.util.Map;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;


/**
 * 灰度线程池工具类
 *
 * @author Sean
 */
public final class CanaryExecutorPool {
    private static final int THREADS = 100;
    private static CanaryExecutorPool instance;
    private final ExecutorService executor = new ThreadPoolExecutor(THREADS / 3, THREADS,
            60L, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000),
            new ThreadFactoryBuilder().setNameFormat("CanaryPool-%d").build());

    private CanaryExecutorPool() {
    }

    /**
     * 获取实例
     *
     * @return 线程池实例
     */
    public static CanaryExecutorPool getInstance() {
        if (instance == null) {
            synchronized (CanaryExecutorPool.class) {
                instance = new CanaryExecutorPool();
            }
        }
        return instance;
    }

    /**
     * 执行FeignClient 调用
     *
     * @param <U>           U
     * @param feignSupplier job
     * @return CompletableFuture
     */
    public <U> CompletableFuture<U> runAsync(Supplier<U> feignSupplier) {
        Map<String, String> headers = RequestHolder.getHeaders();
        String traceId = MDC.get(Logger.KEY_TRACE);
        String spanId = MDC.get(Logger.KEY_SPAN);
        return CompletableFuture.supplyAsync(() -> {
            CanaryInfoHolder.handle(headers, traceId, spanId);
            U result = feignSupplier.get();
            CanaryInfoHolder.cleanHeaders();
            return result;
        }, executor);
    }

    /**
     * 异步 执行FeignClient 调用
     *
     * @param feignRunnable task
     */
    public void runAsync(Runnable feignRunnable) {
        Map<String, String> headers = RequestHolder.getHeaders();
        String traceId = MDC.get(Logger.KEY_TRACE);
        String spanId = MDC.get(Logger.KEY_SPAN);
        executor.execute(() -> {
            CanaryInfoHolder.handle(headers, traceId, spanId);
            feignRunnable.run();
            CanaryInfoHolder.cleanHeaders();
        });
    }
}
